package main

import (
	"debug/elf"
	"fmt"
	"strconv"
)

const fileFilter = "All File(*);;Shared Object File(*.so);;Relocatable File(*.o);;Executable File(*.out)"

type Root = ElfFile

type ElfFile struct {
	Ident        ElfIdent
	Header       ElfHdr
	ProgHdrs     []ElfProgHdr
	SectionDatas []ElfSectionData
	Sections     []ElfSectionHdr
}

func (v *ElfFile) Unmarshal(dec *Decoder, info *ValueInfo) {
	dec.SetArg(v)
	dec.Decode(&v.Ident, &ValueInfo{name: "Ident", info: "文件标识"})
	isBig := v.Ident.IsBigEndian()
	dec.Decode(&v.Header, &ValueInfo{name: "Header", isBig: isBig, info: "ELF头"})

	dec.Seek(int(v.Header.Phoff) * 8)
	v.ProgHdrs = make([]ElfProgHdr, v.Header.Phnum)
	dec.Decode(&v.ProgHdrs, &ValueInfo{name: "ProgHdrs", isBig: isBig, info: "程序头"})

	SectOft := dec.Pos()

	dec.Seek(int(v.Header.Shoff) * 8)
	v.Sections = make([]ElfSectionHdr, v.Header.Shnum)
	dec.Decode(&v.Sections, &ValueInfo{name: "Sections", isBig: isBig, info: "节头"})

	pos := dec.Pos()
	defer dec.Seek(pos)

	dec.Seek(SectOft)
	newDec := dec.SubDecoder(int(v.Header.Shoff) - SectOft/8)
	newDec.Decode(&v.SectionDatas, &ValueInfo{name: "SectionDatas", isBig: isBig})

	for _, sect := range v.SectionDatas {
		hdr := &v.Sections[sect.index]
		data := v.SectionData(hdr)
		if data == nil {
			continue
		}
		dec.Seek(int(hdr.Off) * 8)
		newDec = dec.SubDecoder(int(hdr.Size))
		newDec.Decode(data, &ValueInfo{isBig: isBig})
	}
}

func (v *ElfFile) SectionData(sect *ElfSectionHdr) any {
	switch sect.Name {
	case ".dynamic":
		var buf []ElfDyn
		return &buf
	default:
		break
	}
	switch sect.Type {
	case elf.SHT_SYMTAB, elf.SHT_DYNSYM:
		var buf []ElfSymbol
		return &buf
	case elf.SHT_REL:
		var buf []ElfRelocation
		return &buf
	case elf.SHT_RELA:
		var buf []ElfRelocationA
		return &buf
	default:
		break
	}
	return nil
}

func (v *ElfFile) RawToRva(off int) (uint64, int) {
	for i, sect := range v.Sections {
		if sect.Name == ".bss" {
			continue
		}
		if int(sect.Off) == off {
			return uint64(sect.Addr), i
		}
		if int(sect.Off) < off && off < int(sect.Off+sect.Size) {
			return uint64(sect.Addr) + uint64(off-int(sect.Off)), i
		}
	}
	return 0, -1
}

func (v *ElfFile) SectBuf(index int) []byte {
	for _, sect := range v.SectionDatas {
		if sect.index == index {
			return sect.Data
		}
	}
	return nil
}

type ElfIdent struct {
	Magic      ElfMag
	Class      elf.Class
	Data       elf.Data
	Version    elf.Version
	OsAbi      elf.OSABI
	AbiVersion uint8
	Pad        [7]byte
}

func (v *ElfIdent) Is64BitFile() bool {
	return v.Class == elf.ELFCLASS64
}

func (v *ElfIdent) IsBigEndian() bool {
	return v.Data == elf.ELFDATA2MSB
}

type ElfMag string

func (v *ElfMag) Unmarshal(dec *Decoder, info *ValueInfo) {
	*v = ElfMag(dec.ReadBytes(4))
	if *v != elf.ELFMAG {
		panic("not an ELF file - it has the wrong magic bytes at the start")
	}
}

// ElfHdr : ELF File header.
type ElfHdr struct {
	Type      elf.Type    `info:"类型"`
	Machine   elf.Machine `info:"系统架构"`
	Version   elf.Version `bit:"32" info:"版本"`
	Entry     ElfUint     `info:"程序入口"`
	Phoff     ElfUint     `info:"程序头偏移"`
	Shoff     ElfUint     `info:"节头偏移"`
	Flags     uint32      `info:"标志"`
	Ehsize    uint16      `info:"ELF头大小"`
	Phentsize uint16      `info:"程序头大小"`
	Phnum     uint16      `info:"程序头数"`
	Shentsize uint16      `info:"节头大小"`
	Shnum     uint16      `info:"节头数"`
	Shstrndx  uint16      `info:"字符串节索引"`
}

type ElfUint uint64

func (v *ElfUint) Unmarshal(dec *Decoder, info *ValueInfo) {
	f, ok := dec.Arg().(*ElfFile)
	if !ok || f == nil {
		panic("decode arg not set")
	}

	if f.Ident.Is64BitFile() {
		var t uint64
		dec.Decode(&t, info)
		*v = ElfUint(t)
	} else {
		var t uint32
		dec.Decode(&t, info)
		*v = ElfUint(t)
	}
}

// ElfProgHdr : ELF Program header.
type ElfProgHdr struct {
	Type   elf.ProgType `bit:"32" info:"类型"`
	Flags  elf.ProgFlag `info:"标志"`
	Off    ElfUint      `info:"文件偏移"`
	Vaddr  ElfUint      `info:"内存地址"`
	Paddr  ElfUint
	Filesz ElfUint `info:"文件中大小"`
	Memsz  ElfUint `info:"内存中大小"`
	Align  ElfUint `info:"对齐"`
}

// ElfSectionHdr : ELF Section header.
type ElfSectionHdr struct {
	Name      SectName        `info:"名称"`
	Type      elf.SectionType `info:"类型"`
	Flags     SectionFlag     `info:"标志"`
	Addr      ElfUint         `info:"内存地址"`
	Off       ElfUint         `info:"文件偏移"`
	Size      ElfUint         `info:"大小"`
	Link      uint32          `info:"相关索引"`
	Info      uint32          `info:"信息"`
	Addralign ElfUint         `info:"对齐"`
	Entsize   ElfUint         `info:"每段大小"`
}

func (v *ElfSectionHdr) String() string {
	return string(v.Name)
}

type SectName string

func (v *SectName) Unmarshal(dec *Decoder, info *ValueInfo) {
	f, ok := dec.Arg().(*ElfFile)
	if !ok || f == nil {
		panic("decode arg not set")
	}

	var nameOft uint32
	dec.Decode(&nameOft, info)
	pos := dec.Pos()
	defer dec.Seek(pos)

	oft := int(f.Header.Shoff)
	oft += int(f.Header.Shstrndx * f.Header.Shentsize)
	if f.Ident.Is64BitFile() {
		oft += 24
		dec.Seek(oft * 8)
		var t uint64
		dec.Decode(&t, &ValueInfo{isBig: info.isBig})
		t += uint64(nameOft)
		dec.Seek(int(t) * 8)
	} else {
		oft += 16
		dec.Seek(oft * 8)
		var t uint32
		dec.Decode(&t, &ValueInfo{isBig: info.isBig})
		t += nameOft
		dec.Seek(int(t) * 8)
	}

	str := ""
	dec.Decode(&str, &ValueInfo{name: "String", info: "节名"})
	*v = SectName(str)
}

type SectionFlag ElfUint

func (v SectionFlag) String() string {
	return elf.SectionFlag(v).String()
}

func (v *SectionFlag) Unmarshal(dec *Decoder, info *ValueInfo) {
	var flag ElfUint
	dec.Decode(&flag, info)
	*v = SectionFlag(flag)
}

type ElfSectionData struct {
	CmpHdr *ElfCmpHdr
	Data   []byte
	Space  []byte
	index  int
	name   string
}

func (v ElfSectionData) String() string {
	return v.name
}

func (v *ElfSectionData) Unmarshal(dec *Decoder, info *ValueInfo) {
	f, ok := dec.Arg().(*ElfFile)
	if !ok || f == nil {
		panic("decode arg not set")
	}

	off := (dec.Pos() + 7) / 8
	v.index = -1
	for i, sect := range f.Sections {
		if sect.Name == ".bss" {
			continue
		}
		if int(sect.Off) == off {
			v.index = i
			break
		}
	}
	if v.index < 0 {
		panic("unknow section at: " + strconv.Itoa(off))
	}

	info.name = strconv.Itoa(v.index)
	v.name = string(f.Sections[v.index].Name)
	size := int(f.Sections[v.index].Size)
	if elf.SectionFlag(f.Sections[v.index].Flags)&elf.SHF_COMPRESSED != 0 {
		v.CmpHdr = &ElfCmpHdr{}
		dec.Decode(&v.CmpHdr, &ValueInfo{name: "CmpHdr", isBig: info.isBig})
		size -= dec.Pos()/8 - off
	}
	v.Data = dec.ReadBytes(size)
	if v.index+1 < int(f.Header.Shnum) {
		space := int(f.Sections[v.index+1].Off) - dec.Pos()/8
		if space > 0 {
			newDec := dec.SubDecoder(space)
			newDec.Decode(&v.Space, &ValueInfo{name: "Space"})
			dec.Seek(newDec.Pos())
		}
	} else if !dec.IsEof() {
		dec.Decode(&v.Space, &ValueInfo{name: "Space"})
	}
}

// ElfCmpHdr : ELF Compression header.
type ElfCmpHdr struct {
	Type      CompressionType
	Size      ElfUint
	Addralign ElfUint
}

type CompressionType uint32

func (v CompressionType) String() string {
	return elf.CompressionType(v).String()
}

func (v *CompressionType) Unmarshal(dec *Decoder, info *ValueInfo) {
	f, ok := dec.Arg().(*ElfFile)
	if !ok || f == nil {
		panic("decode arg not set")
	}

	var t uint32
	dec.Decode(&t, info)
	*v = CompressionType(t)
	if f.Ident.Is64BitFile() {
		dec.Decode(&t, nil)
	}
}

// ElfDyn : ELF Dynamic structure. The ".dynamic" section contains an array of them.
type ElfDyn struct {
	Tag DynTag
	Val ElfUint
}

type DynTag ElfUint

func (v DynTag) String() string {
	return elf.DynTag(v).String()
}

func (v *DynTag) Unmarshal(dec *Decoder, info *ValueInfo) {
	var flag ElfUint
	dec.Decode(&flag, info)
	*v = DynTag(flag)
}

// ElfSymbol : ELF Symbol.
type ElfSymbol struct {
	Name  SymName
	Info  SymInfo
	Other SymOther
	Shndx uint16
	Value uint64
	Size  uint64
}

func (v *ElfSymbol) String() string {
	return string(v.Name)
}

func (v *ElfSymbol) Unmarshal(dec *Decoder, info *ValueInfo) {
	f, ok := dec.Arg().(*ElfFile)
	if !ok || f == nil {
		panic("decode arg not set")
	}

	if f.Ident.Is64BitFile() {
		dec.Decode(&v.Name, &ValueInfo{name: "Name", isBig: info.isBig})
		dec.Decode(&v.Info, &ValueInfo{name: "Info"})
		dec.Decode(&v.Other, &ValueInfo{name: "Other"})
		dec.Decode(&v.Shndx, &ValueInfo{name: "Shndx", isBig: info.isBig})
		dec.Decode(&v.Value, &ValueInfo{name: "Value", isBig: info.isBig})
		dec.Decode(&v.Size, &ValueInfo{name: "Size", isBig: info.isBig})
	} else {
		var tmp uint32
		dec.Decode(&v.Name, &ValueInfo{name: "Name", isBig: info.isBig})
		dec.Decode(&tmp, &ValueInfo{name: "Value", isBig: info.isBig})
		v.Value = uint64(tmp)
		dec.Decode(&tmp, &ValueInfo{name: "Size", isBig: info.isBig})
		v.Size = uint64(tmp)
		dec.Decode(&v.Info, &ValueInfo{name: "Info"})
		dec.Decode(&v.Other, &ValueInfo{name: "Other"})
		dec.Decode(&v.Shndx, &ValueInfo{name: "Shndx", isBig: info.isBig})
	}
}

type SymName string

func (v *SymName) Unmarshal(dec *Decoder, info *ValueInfo) {
	f, ok := dec.Arg().(*ElfFile)
	if !ok || f == nil {
		panic("decode arg not set")
	}

	var name uint32
	dec.Decode(&name, info)
	_, index := f.RawToRva(dec.Pos() / 8)
	if index < 0 {
		panic("unknow section at: " + strconv.Itoa(dec.Pos()/8))
	}

	link := int(f.Sections[index].Link)
	strSectData := f.SectBuf(link)
	if len(strSectData) == 0 {
		panic("unknow section of index:" + strconv.Itoa(link))
	}
	newDec := NewDecoder(dec.ItemAddFunc(), strSectData, int(f.Sections[link].Off))

	str := ""
	if name > 0 {
		_ = newDec.ReadBytes(int(name))
	}
	newDec.Decode(&str, &ValueInfo{name: "SymName", isBig: info.isBig})
	*v = SymName(str)
}

type SymInfo struct {
	Type elf.SymType `bit:"4"`
	Bind elf.SymBind `bit:"4"`
}

type SymOther struct {
	Visibility elf.SymVis `bit:"2"`
	Other      uint8      `bit:"6"`
}

type ElfRelocation struct {
	Off  ElfUint
	Info RelInfo
}

type ElfRelocationA struct {
	Off    ElfUint
	Info   RelInfo
	Addend ElfUint
}

type RelInfo struct {
	Type   RelType
	SymIdx uint32
}

func (v *RelInfo) Unmarshal(dec *Decoder, info *ValueInfo) {
	f, ok := dec.Arg().(*ElfFile)
	if !ok || f == nil {
		panic("decode arg not set")
	}

	if f.Ident.Is64BitFile() {
		if info.isBig {
			dec.Decode(&v.SymIdx, &ValueInfo{name: "SymIdx", isBig: true})
			dec.Decode(&v.Type, &ValueInfo{name: "Type", isBig: true})
		} else {
			dec.Decode(&v.Type, &ValueInfo{name: "Type"})
			dec.Decode(&v.SymIdx, &ValueInfo{name: "SymIdx"})
		}
	} else {
		if info.isBig {
			dec.Decode(&v.SymIdx, &ValueInfo{name: "SymIdx", isBig: true, bit: 24})
			dec.Decode(&v.Type, &ValueInfo{name: "Type", isBig: true, bit: 8})
		} else {
			dec.Decode(&v.Type, &ValueInfo{name: "Type", bit: 8})
			dec.Decode(&v.SymIdx, &ValueInfo{name: "SymIdx", bit: 24})
		}
	}
}

type RelType struct {
	mach elf.Machine
	V    uint32
}

func (v RelType) String() string {
	switch v.mach {
	case elf.EM_X86_64:
		return elf.R_X86_64(v.V).String()
	case elf.EM_AARCH64:
		return elf.R_AARCH64(v.V).String()
	case elf.EM_ALPHA:
		return elf.R_ALPHA(v.V).String()
	case elf.EM_ARM:
		return elf.R_ARM(v.V).String()
	case elf.EM_386:
		return elf.R_386(v.V).String()
	case elf.EM_MIPS:
		return elf.R_MIPS(v.V).String()
	case elf.EM_LOONGARCH:
		return elf.R_LARCH(v.V).String()
	case elf.EM_PPC:
		return elf.R_PPC(v.V).String()
	case elf.EM_PPC64:
		return elf.R_PPC64(v.V).String()
	case elf.EM_RISCV:
		return elf.R_RISCV(v.V).String()
	case elf.EM_S390:
		return elf.R_390(v.V).String()
	case elf.EM_SPARCV9:
		return elf.R_SPARC(v.V).String()
	default:
		return fmt.Sprintf("0x%X(%d)", v.V, v.V)
	}
}

func (v *RelType) Unmarshal(dec *Decoder, info *ValueInfo) {
	f, ok := dec.Arg().(*ElfFile)
	if !ok || f == nil {
		panic("decode arg not set")
	}
	dec.Decode(&v.V, info)
	v.mach = f.Header.Machine
}
